#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include "ChessGame.h"
#include "pugixml.hpp"

ChessGame::ChessGame() {
	board = new Board();
	pieces = new Pieces(board);
	moveHistory = new MoveHistory();
	currentPlayersColor = WHITE;
}
ChessGame::ChessGame(const char* filePointer){
	board = new Board();
	pieces = new Pieces(board,"defaultload");
	moveHistory = new MoveHistory();
	currentPlayersColor = WHITE;
	loadFromXml(filePointer);
}
ChessGame::~ChessGame() {
	delete board;
	delete pieces;
	delete moveHistory;
}
bool ChessGame::currentPlayerIsHuman(){
	if(currentPlayersColor == WHITE && whitePlayer == "human")
		return true;
	else if(currentPlayersColor == BLACK && blackPlayer == "human")
		return true;
	else 
		return false;
}
bool ChessGame::loadFromXml(const char* filePointer){
	pugi::xml_document doc;
	pugi::xml_parse_result result = doc.load_file(filePointer);

	pugi::xml_node boardnode = doc.child("chessgame").child("history").child("move");//.child("piece")
	
	while(boardnode != NULL){
		pugi::xml_node tempBoardnode = boardnode.child("piece");

		string type = tempBoardnode.attribute("type").value();
		string color = tempBoardnode.attribute("color").value();
		std::transform(color.begin(), color.end(), color.begin(), ::toupper);
		currentPlayersColor = (color == "WHITE") ? BLACK : WHITE;

		int homecolumn = atoi(tempBoardnode.attribute("column").value());
		int homerow= atoi(tempBoardnode.attribute("row").value());



		tempBoardnode = tempBoardnode.next_sibling("piece");
		int destcolumn = atoi(tempBoardnode.attribute("column").value());
		int destrow= atoi(tempBoardnode.attribute("row").value());
		//create new moverecord with specs above
		tempBoardnode = tempBoardnode.next_sibling("piece");
		
		MoveRecord * tempRecord = new MoveRecord(board->GetCell(homerow, homecolumn) , board->GetCell(destrow, destcolumn), color, type);
		
		if(tempBoardnode != NULL){
			Piece * tempPiece;
			tempPiece = pieces->AddPiece(tempBoardnode.attribute("type").value(), tempBoardnode.attribute("color").value(),-1,-1);
			tempRecord->setDisplacedPiece(tempPiece);
			tempRecord->setDispColor(tempBoardnode.attribute("color").value());
			tempRecord->setDispType(tempBoardnode.attribute("type").value());
		}
		
		moveHistory->AddMoveRecord(tempRecord);
		boardnode = boardnode.next_sibling("move");
	}


	boardnode = doc.child( "chessgame" ).child("board").child("piece");
	while(boardnode != NULL){
		string type = boardnode.attribute("type").value();
		string color = boardnode.attribute("color").value();
		string column = boardnode.attribute("column").value();
		string row= boardnode.attribute("row").value();
		
		pieces->AddPiece(type,color,atoi(row.c_str()),atoi(column.c_str()));
		boardnode = boardnode.next_sibling("piece");
	}

	return true;
}

Cell* ChessGame::GetCell(int row, int col) {
	return board->GetCell( row,col );
}
Piece* ChessGame::GetPiece(int row, int col) {
	if (board->GetCell(row, col) == NULL) {
		return NULL;
	}
	return board->GetCell( row,col )->GetPiece();
}
Pieces* ChessGame::GetPieces() {
	return pieces;
}

PieceColor ChessGame::GetCurrentPlayersColor() {
	return currentPlayersColor;
}
PieceColor ChessGame::GetOtherPlayersColor(){
	PieceColor tempcolor;
	tempcolor = (currentPlayersColor == WHITE) ? BLACK : WHITE;
	return tempcolor;
}

bool ChessGame::CellHasCurrentPlayersPiece(Cell* cell) {
	return (cell->GetPiece()->GetColor() == currentPlayersColor);
}

MoveRecord* ChessGame::Move(Piece* piece, Cell* destCell) {
	MoveRecord* moveRecord = new MoveRecord(piece->GetCell(), destCell,piece->getColorString(),piece->getTypeString());

	piece->GetCell()->BindToPiece(NULL);
	piece->BindToCell(destCell);
	if (destCell->GetPiece() == NULL) {
		destCell->BindToPiece(piece);
	} else {
		moveRecord->setDispColor(destCell->GetPiece()->getColorString());
		moveRecord->setDispType(destCell->GetPiece()->getTypeString());
		destCell->GetPiece()->BindToCell(NULL);
		destCell->BindToPiece(piece);
	}
	
	// now it's the other player's turn
	currentPlayersColor = (currentPlayersColor == WHITE) ? BLACK : WHITE;
	moveHistory->AddMoveRecord(moveRecord);
	return moveRecord;
}
void ChessGame::UndoMove(MoveRecord* moveRecord) {
	if(moveRecord == NULL)
		return;

	Piece * movedPiece = moveRecord->GetDestCell()->GetPiece();
	//PROBLEM IS HERE!!!!!! OMG! :/
	//When loading the history I need to create a new piece for each displaced one and bind it to the record
	// undo everything the given move did
	if (moveRecord->GetDisplacedPiece() == NULL) {
		moveRecord->GetDestCell()->BindToPiece(NULL);
	} else {
		moveRecord->GetDestCell()->BindToPiece(moveRecord->GetDisplacedPiece());
		moveRecord->GetDisplacedPiece()->BindToCell(moveRecord->GetDestCell());
	}
	
	movedPiece->BindToCell(moveRecord->GetHomeCell());
	moveRecord->GetHomeCell()->BindToPiece(movedPiece);
	
	// now it's the other player's turn again
	currentPlayersColor = (currentPlayersColor == WHITE) ? BLACK : WHITE;
	
	// delete the move record created by the last move
	delete moveRecord;
}

bool ChessGame::PlayerInCheck(PieceColor playersColor) {
	// run through each cell on the board
	for (int i = 0; i < 8; i++) {		
		for (int j = 0; j < 8; j++) {
			// if the cell has the other player's piece
			if (board->GetCell( i,j )->GetPiece() != NULL && 
				board->GetCell( i,j )->GetPiece()->GetColor() != playersColor )
			{
				// if the piece has this player's king's cell as a valid move
				if (playersColor == WHITE &&
					board->GetCell( i,j )->GetPiece()->ValidMovesContains(pieces->gWhiteKing()->GetCell()) )
				{
					return true;
				}
				else if (playersColor == BLACK &&
					board->GetCell( i,j )->GetPiece()->ValidMovesContains(pieces->gBlackKing()->GetCell()) )
				{
					return true;
				}
			}
		}
	}
	
	return false;
}
bool ChessGame::PlayerInCheckMate(PieceColor playersColor) {
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 8; j++) {
			// if any pieces of this color still have valid moves
			if (GetPiece( i,j ) != NULL && 
				GetPiece( i,j )->GetColor() == playersColor && 
				!GetValidMoves(GetPiece( i,j ))->empty() )
			{
				// we are not in checkmate
				return false;
			}
		}
	}
	
	// otherwise we are in checkmate
	return true;
}
std::set<Cell*>* ChessGame::GetValidMoves(Piece* piece) {

	std::set<Cell*> tmp;
	// run through the set of previously valid moves
	std::set<Cell*>* validMoves = piece->GetValidMoves();
	std::set<Cell*>::iterator it = validMoves->begin();

	while (it != validMoves->end()) {
		// if the move won't result in check for the given player, 
		if (!MoveEndsUpInCheck(piece, *it, currentPlayersColor)) {
			// it has passed the final test of becoming a valid move
			tmp.insert(*it);
		}
		it++;
	}
	
	validMoves->swap(tmp);
	return validMoves;
}
bool ChessGame::IsValidMove(Piece* piece, Cell* destCell) {
	std::set<Cell*>* validMoves = GetValidMoves(piece);
	return (validMoves->find(destCell) != validMoves->end());
}
bool ChessGame::MoveEndsUpInCheck(Piece* piece, Cell* cell, PieceColor playersColor) {

	bool resultsInCheck;
	MoveRecord* moveRecord = TempMove(piece, cell);	
	resultsInCheck = PlayerInCheck(playersColor);

	

	UndoMove(moveRecord);

	return resultsInCheck;
}

MoveRecord* ChessGame::PopMostRecentMoveRecord() {
	return moveHistory->PopMostRecentMoveRecord();
}

MoveRecord* ChessGame::TempMove(Piece* piece, Cell* destCell) {
	MoveRecord* moveRecord = new MoveRecord(piece->GetCell(), destCell,piece->getColorString(),piece->getTypeString());

	piece->GetCell()->BindToPiece(NULL);
	piece->BindToCell(destCell);
	if (destCell->GetPiece() == NULL) {
		destCell->BindToPiece(piece);
	} else {
		destCell->GetPiece()->BindToCell(NULL);
		destCell->BindToPiece(piece);
	}
	
	// now it's the other player's turn
	currentPlayersColor = (currentPlayersColor == WHITE) ? BLACK : WHITE;
	return moveRecord;
}

bool ChessGame::saveToXml(string loc){
	string OutFile = loc;
	ofstream Output(OutFile.c_str());
	
	string output = "<chessgame>";
	output += this->board->toString();
	output += this->moveHistory->toXmlString();
	output += "</chessgame>";

	Output << output << endl;
	Output.close();

	return true;
}
void ChessGame::SetGameMode(char *  newMode){
	int intMode = atoi(newMode);
	if(intMode == 0){
		this->blackPlayer = "human";
		this->whitePlayer = "human";
	} else if(intMode == 1){
		this->blackPlayer = "computer";
		this->whitePlayer = "human";
	} else if(intMode == 2){
		this->blackPlayer = "human";
		this->whitePlayer = "computer";
	} else if(intMode == 3){
		this->blackPlayer = "computer";
		this->whitePlayer = "computer";
	}
}